← Writeups

DOM XSS in jQuery anchor href attribute sink using location.search source

Abuso de un sink jQuery .attr('href') controlado por location.search: el parámetro returnPath se inyecta como javascript:alert(document.cookie) en el enlace Back.

Target Overview & Vulnerability Summary

El laboratorio contiene un DOM XSS en la página de envío de feedback. jQuery selecciona un elemento ancla con el selector $ y cambia su atributo href usando datos de location.search (parámetro returnPath). Al inyectar un esquema javascript:, el enlace "Back" ejecuta código cuando la víctima hace clic.

Campo Valor
Severidad Media-Alta (ejecución de JavaScript al interactuar con el enlace)
Endpoint vulnerable /feedback (parámetro returnPath)
Root cause Sink jQuery .attr() sobre href con source location.search sin validación de esquema

Technical Breakdown & Exploitation Flow

1. El sink

Dentro de la ruta de feedback:

<script>
$(function() {
    $('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});
</script>
  • attr() es la función de jQuery que manipula atributos del DOM.
  • El selector $('#backLink') encuentra el elemento ancla del enlace "Back".
  • El valor del parámetro returnPath de la URL se asigna directo al atributo href.

2. Payload

URL/feedback?returnPath=javascript:alert(document.cookie)

Al asignar href = "javascript:alert(document.cookie)", el enlace "Back" adquiere un esquema pseudo-protocolo. Cuando el usuario hace clic, el navegador interpreta el esquema javascript: y ejecuta el código en el contexto del documento.

Root Cause Analysis

El defecto es asignar a un atributo de URL (contexto de destino de navegación) un valor controlado por el usuario sin validar el esquema. El navegador distingue protocolos activos (javascript:, data:, vbscript:) de esquemas seguros (https:, mailto:), y un href acepta cualquiera de ellos. location.search es un source directamente modificable desde la URL, y el sink .attr('href') no aplica ningún filtro.

Secure Remediation

  • Validar el esquema del returnPath antes de asignarlo:
$(function() {
    const returnPath = new URLSearchParams(window.location.search).get('returnPath');
    if (returnPath && returnPath.startsWith('/')) {
        $('#backLink').attr("href", returnPath);
    }
});
  • Idealmente, resolver el returnPath server-side contra una lista blanca de rutas internas y nunca reflejarlo sin validar. La regla es: cualquier atributo que contenga una URL debe usar un esquema de lista blanca (https:, rutas relativas) y nunca aceptar protocolos activos.
  • Como refuerzo, CSP script-src que bloquee javascript: URLs inline o navegación a esquemas activos.